<?php

use App\Constants\MiniProgramConstant;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Str;

if (!function_exists('getSettings')) {
    function getSettings(string $group, ?string $name = null)
    {
        $data = Cache::tags('settings')->get($group);
        if (!$data) {
            $data = \App\Models\Settings::where('group', '=', $group)->pluck('value', 'name')->all();
            if (!$data) {
                return null;
            }
            Cache::tags('settings')->put($group, $data, 3600);
        }

        return is_null($name) ? $data : \Illuminate\Support\Arr::get($data, $name);
    }
}

if (!function_exists('flushSettings')) {
    function flushSettings(?string $group = null)
    {
        if (is_null($group)) {
            Cache::tags('settings')->flush();
        } else {
            Cache::tags('settings')->forget($group);
        }
    }
}

if (!function_exists('getMiniSettings')) {
    function getMiniSettings(string $miniType, string $group, ?string $name = null)
    {
        $key = "{$miniType}-{$group}";
        $data = Cache::tags('settings')->get($key);
        if (!$data) {
            $data = \App\Models\Settings::where(['group' => $group, 'mini_type' => $miniType])->pluck('value', 'name')->all();
            if (!$data) {
                return null;
            }
            Cache::tags('settings')->put($key, $data, 3600);
        }

        return is_null($name) ? $data : \Illuminate\Support\Arr::get($data, $name);
    }
}

if (!function_exists('flushMiniSettings')) {
    function flushMiniSettings(?string $miniType = null, ?string $group = null)
    {
        if (is_null($miniType) && is_null($group)) {
            Cache::tags('settings')->flush();
        } else {
            Cache::tags('settings')->forget("{$miniType}-{$group}");
        }
    }
}
if (!function_exists('getAuthMiniProgramMap')) {
    function getAuthMiniProgramMap(int $adminId)
    {
        $key = MiniProgramConstant::CACHE_KEY;
        $miniPrograms = Cache::get($key);
        if (!$miniPrograms) {
            $miniPrograms = \App\Models\MiniProgram::pluck('name', 'slug')->all();
            Cache::put($key, $miniPrograms, 3600);
        }

        $slugs = \App\Models\AdminMiniProgram::where('admin_id', '=', $adminId)->pluck('mini_type')->all();

        $data = [];

        foreach ($slugs as $slug) {
            $data[$slug] = $miniPrograms[$slug] ?? '';
        }

        return $data;
    }
}

if (!function_exists('getMiniAdvIds')) {
    function getMiniAdvIds(string $miniType): array
    {
        $setting = getMiniSettings($miniType, \App\Constants\AdvConfigConstant::ADV_GROUP);

        return [
            \App\Constants\AdvConfigConstant::BANNER_ADV_ID    => $setting[\App\Constants\AdvConfigConstant::BANNER_ADV_ID] ?? '',
            \App\Constants\AdvConfigConstant::INCENTIVE_ADV_ID => $setting[\App\Constants\AdvConfigConstant::INCENTIVE_ADV_ID] ?? '',
            \App\Constants\AdvConfigConstant::SCREEN_ADV_ID    => $setting[\App\Constants\AdvConfigConstant::SCREEN_ADV_ID] ?? '',
            \App\Constants\AdvConfigConstant::VIDEO_ADV_ID     => $setting[\App\Constants\AdvConfigConstant::VIDEO_ADV_ID] ?? '',
        ];
    }
}

if (!function_exists('get_stack_traces')) {
    /**
     * 根据回溯跟踪调用位置
     * @param integer $forgetLines [description]
     * @param null $ignoredPaths
     * @return array [type]               [description]
     */
    function get_stack_traces($forgetLines = 0, $ignoredPaths = null)
    {
        $trace = collect(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS))->forget($forgetLines);

        $ignoredPaths = $ignoredPaths ?? base_path('vendor' . DIRECTORY_SEPARATOR . 'laravel');

        return $trace->filter(function ($frame) use ($ignoredPaths) {
            if (!isset($frame['file'])) {
                return false;
            }

            return !Str::contains($frame['file'],
                $ignoredPaths
            );
        })->values()->all();
    }
}

if (!function_exists('get_caller_from_stack_trace')) {
    /**
     * 根据回溯跟踪调用位置
     * @param integer $forgetLines [description]
     * @param null $ignoredPaths
     * @return mixed [type]               [description]
     */
    function get_caller_from_stack_trace($forgetLines = 0, $ignoredPaths = null)
    {
        $trace = collect(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS))->forget($forgetLines);

        $ignoredPaths = $ignoredPaths ?? base_path('vendor' . DIRECTORY_SEPARATOR . 'laravel');

        return $trace->first(function ($frame) use ($ignoredPaths) {
            if (!isset($frame['file'])) {
                return false;
            }

            return !Str::contains($frame['file'],
                $ignoredPaths
            );
        });
    }
}

if (!function_exists('log_exception')) {
    /**
     * 记录异常
     */
    function log_exception($log_message, $e, array $parameters = [])
    {
        if (!$e instanceof \Exception) {
            // 转下，有可能为throwable类型
            $e = new \Symfony\Component\Debug\Exception\FatalThrowableError($e);
        }
        $caller = get_caller_from_stack_trace();
        // [$caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS & ~DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
        // [$caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
        Log::error($log_message, array_merge([
            // 请求URL
            'requestUrl'    => Request::fullUrl(),
            // 请求方法
            'requestMethod' => Request::method(),
            "\r\n",
            // 调用的类的位置，行数
            'caller'        => ($caller['file'] ?? '') . " " . ($caller['line'] ?? ''),
            'exception'     => "Message:" . $e->getMessage() . " File:" . $e->getFile() . " Line:" . $e->getLine(),
            "\r\n",
            'traces'        => array_slice($e->getTrace(), 0, 5),
        ], $parameters));
    }
}

if (!function_exists('isQuickComment')) {
    function isQuickComment(string $content, string $miniType): bool
    {
        return \App\Models\CommonWords::where(['content' => $content, 'mini_type' => $miniType])
            ->where('status', '=', true)
            ->exists();
    }
}

if (!function_exists('cutKeyword')) {
    function cutKeyword(string $keyword): string
    {
        $keyword = str_replace(" ", "", $keyword);
        $wordLength = mb_strlen($keyword, 'UTF-8');

        $subStr = [$keyword];
        for ($i = 0; $i < $wordLength; $i++) {
            $subStr[] = mb_substr($keyword, $i, 1, 'UTF-8');
        }

        return join("|", $subStr);
    }
}

if (!function_exists('ngrokUrl')) {
    function ngrokUrl($routeName, $parameters = [])
    {
        if (app()->environment('local') && $url = config('app.ngrok_url')) {
            return $url . route($routeName, $parameters, false);
        }

        return route($routeName, $parameters);
    }
}

if (!function_exists('generateUniqueSn')) {
    function generateUniqueSn(): string
    {
        [$us, $s] = explode(' ', microtime());

        $datetime = date('YmdHis', $s);

        return (string)$datetime . str_pad($us * 1000000, 6, '0', \STR_PAD_LEFT) . random_int(1000, 9999);
    }
}

if (!function_exists('resultSuccessReturn')) {
    function resultSuccessReturn(string $msg = 'success', $data = null, array $extends = []): array
    {
        $status = true;
        return array_merge($extends, compact('status', 'data', 'msg'));
    }
}

if (!function_exists('resultFailedReturn')) {
    function resultFailedReturn(string $msg = 'failed', $data = null, array $extends = []): array
    {
        $status = false;
        return array_merge($extends, compact('status', 'data', 'msg'));
    }
}

if (!function_exists('isCloseAdv')) {
    function isCloseAdv(int $memberId, string $miniType)
    {
        $info = \App\Models\Member::where('id', $memberId)->first(['is_vip', 'vip_show_adv']);
        if (!$info || !$info->is_vip) return false;

        return $info->vip_show_adv == 0;
    }
}

if (!function_exists('yuan_to_fen')) {
    function yuan_to_fen($number)
    {
        return bcadd($number * 100, '0', 0);
    }
}

if (!function_exists('fen_to_yuan')) {
    function fen_to_yuan($number)
    {
        return bcdiv($number, 100, 2);
    }
}

if (!function_exists('get_wechat_pay_config')) {
    function get_wechat_pay_config(string $miniType): array
    {
        $settings = getMiniSettings($miniType, MiniProgramConstant::GROUP);

        $certPath = $settings[MiniProgramConstant::APP_CERT_PATH] ?? '';
        $keyPath = $settings[MiniProgramConstant::APP_KEY_PATH] ?? '';

        if ($certPath) {
            $certPath = storage_path('app/' . $certPath);
        }
        if ($keyPath) {
            $keyPath = storage_path('app/' . $keyPath);
        }

        return [
            'app_id'    => $settings[MiniProgramConstant::APP_ID] ?? '',
            'mch_id'    => $settings[MiniProgramConstant::APP_MCH_ID] ?? '',
            'key'       => $settings[MiniProgramConstant::APP_KEY] ?? '',
            'cert_path' => $certPath,
            'key_path'  => $keyPath,
        ];
    }
}

if (!function_exists('publisher_options')) {
    function publisher_options(): array
    {
        $publishers = \App\Models\Publisher::with(['member'])->get();

        $miniTypeArr = array_unique(array_filter($publishers->pluck('member')->pluck('mini_type')->all()));
        $miniArr = \App\Models\MiniProgram::whereIn('slug', $miniTypeArr)->where('status', 1)->pluck('name', 'slug')->all();

        $data = [];

        foreach ($publishers as $publisher) {
            if (!$publisher->member || !$publisher->member->mini_type) continue;

            $miniName = $miniArr[$publisher->member->mini_type] ?? '';
            if (!$miniName) continue;

            $data[sprintf('%s|%s', $publisher->id, $publisher->member->mini_type)] = sprintf('%s %s', "【{$miniName}】", $publisher->name);
        }

        return $data;
    }
}

if (!function_exists('member_options')) {
    function member_options(): array
    {
        $members = \App\Models\Member::with('mini_program')
            ->select(['id', 'nickname', 'mini_type'])
            ->whereHas('mini_program', function ($q) {
                return $q->where('status', 1);
            })->get();

        $data = [];

        foreach ($members as $member) {
            $data[sprintf('%s|%s', $member->id, $member->mini_type)] = sprintf('%s %s', "【{$member->mini_program->name}】", $member->nickname);
        }

        return $data;
    }
}
